<!--
 * @Description  : 
 * @Version      : 1.0
 * @Author       : seraph
 * @Date         : 2024-12-26 12:19:10
 * @LastEditors  : seraph
 * @LastEditTime : 2025-01-17 15:19:02
-->
<template>
  <h3>基础示例</h3>
  <p>
    模板中的表达式虽然方便，但也只能用来做简单的操作。如果在模板中写太多逻辑，会让模板变得臃肿，难以维护。
  </p>
  <p>比如说，我们有这样一个包含嵌套数组的对象：</p>
  <pre><code class="javascript" v-html="highlighted1"></code></pre>
  <p>我们想根据 author 是否已有一些书籍来展示不同的信息：</p>
  <pre><code class="javascript" v-html="highlighted2"></code></pre>
  <p>
    这里的模板看起来有些复杂。我们必须认真看好一会儿才能明白它的计算依赖于
    author.books。更重要的是，如果在模板中需要不止一次这样的计算，我们可不想将这样的代码在模板里重复好多遍。
  </p>
  <p>因此我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑。这是重构后的示例：</p>
  <pre><code class="javascript" v-html="highlighted3"></code></pre>
  <p>
    我们在这里定义了一个计算属性 publishedBooksMessage。computed() 方法期望接收一个
    <span class="bolder red">getter 函数</span>，返回值为一个计算属性
    <span class="bolder red">ref</span>。和其他一般的 ref 类似，你可以通过
    publishedBooksMessage.value 访问计算结果。计算属性 ref
    也会在模板中自动解包，因此在模板表达式中引用时无需添加 .value。
  </p>
  <p>
    Vue 的计算属性会自动追踪响应式依赖。它会检测到 publishedBooksMessage 依赖于 author.books，所以当
    author.books 改变时，任何依赖于 publishedBooksMessage 的绑定都会同时更新。
  </p>
  <hr />
  <h4 class="bolder red">为 computed() 标注类型</h4>
  <p>computed() 会自动从其计算函数的返回值上推导出类型：</p>
  <pre><code class="javascript" v-html="highlighted4"></code></pre>

  <p>你还可以通过泛型参数显式指定类型：</p>
  <pre><code class="javascript" v-html="highlighted5"></code></pre>
  <p>他有出版过书吗？{{ publishedBooksMessage }}</p>
</template>

<script lang="ts" setup>
import { ref, reactive, nextTick, shallowRef, watch, computed } from 'vue'
import hljs from 'highlight.js'
import 'highlight.js/styles/atom-one-dark.css'
import javascript from 'highlight.js/lib/languages/javascript'

hljs.registerLanguage('javascript', javascript)
hljs.registerLanguage('html', javascript)

const rawCode1 = `const author = reactive({
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
    'Vue 3 - Basic Guide',
    'Vue 4 - The Mystery'
  ]
})`

const highlighted1 = ref(
  hljs
    .highlight(rawCode1, { language: 'javascript' })
    .value.replace(/&amp;gt;/g, '&gt;')
    .replace(/&amp;lt;/g, '&lt;'),
)

const rawCode2 = `<p>Has published books:</p>
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>`

const highlighted2 = ref(
  hljs
    .highlight(rawCode2, { language: 'javascript' })
    .value.replace(/&amp;gt;/g, '&gt;')
    .replace(/&amp;lt;/g, '&lt;'),
)

const rawCode3 = `<script setup>
import { reactive, computed } from 'vue'

const author = reactive({
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
    'Vue 3 - Basic Guide',
    'Vue 4 - The Mystery'
  ]
})

// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
  return author.books.length > 0 ? 'Yes' : 'No'
})
</script&gt;

<template>
  <p>Has published books:</p>
  <span>{{ publishedBooksMessage }}</span>
</template>`

const highlighted3 = ref(
  hljs
    .highlight(rawCode3, { language: 'javascript' })
    .value.replace(/&amp;gt;/g, '&gt;')
    .replace(/&amp;lt;/g, '&lt;'),
)

const rawCode4 = `import { ref, computed } from 'vue'

const count = ref(0)
// 推导得到的类型：ComputedRef&lt;number&gt;
const double = computed(() =&gt; count.value * 2)
// =&gt; TS Error: Property 'split' does not exist on type 'number'
// 属性 split 不能应用于 number
const result = double.value.split('')`

const highlighted4 = ref(
  hljs
    .highlight(rawCode4, { language: 'javascript' })
    .value.replace(/&amp;gt;/g, '&gt;')
    .replace(/&amp;lt;/g, '&lt;'),
)

const rawCode5 = `const double = computed<number>(() => {
// 若返回值不是 number 类型则会报错
})`

const highlighted5 = ref(
  hljs
    .highlight(rawCode5, { language: 'javascript' })
    .value.replace(/&amp;gt;/g, '&gt;')
    .replace(/&amp;lt;/g, '&lt;'),
)

const author = reactive({
  name: 'John Doe',
  books: ['Vue 2 - Advanced Guide', 'Vue 3 - Basic Guide', 'Vue 4 - The Mystery'],
})

const publishedBooksMessage = computed(() => {
  return author.books.length > 0 ? 'Yes' : 'No'
})
</script>

<style scoped>
p {
  font-size: 16px;
  padding: 2px;
  margin: 5px 0;
}

/* 设置预格式化文本的样式 */
pre {
  /* 背景色 */
  background-color: #f1ffed;
  /* 边框 */
  border: 1px solid #ccc;
  /* 缩小内边距 */
  padding: 5px 5px 5px 10px;
  /* 允许滚动 */
  overflow: auto;
  /* 可选：设置上下外边距 */
  margin: 0px 0;
  /* 允许换行 */
  /* normal: 连续的空白字符会被合并为一个空格。文本会在允许的断点处自动换行。
     nowrap：连续的空白字符会被合并为一个空格。文本不会自动换行，即使在允许的断点处也不会换行。
     pre：保留所有的空白字符（包括空格、制表符和换行符）。文本只会在遇到换行符或者 <br> 标签时换行。
     pre-wrap：保留所有的空白字符。文本会在遇到换行符、<br> 标签，或者达到容器边界时换行。
     pre-line：合并连续的空白字符为一个空格，但保留换行符。文本会在遇到换行符、<br> 标签，或者达到容器边界时换行。
   */
  white-space: pre-wrap;
  /* 行高 */
  line-height: 120%;
  /* 定义缩进 */
  tab-size: 4;
}

h3,
h4,
h5,
h6 {
  margin: 25px 0 10px 0;
  line-height: 100%;
}

.bolder {
  font-weight: bolder;
}

.red {
  color: red;
}

button {
  margin: 5px 5px 5px 10px;
  line-height: 150%;
  width: auto;
  padding: 0 10px;
}
</style>
